package com.qihetimes.earthangel.service.impl;

import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaPhoneNumberInfo;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.util.DesensitizedUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.hiwepy.qqwry.spring.boot.ext.IPZone;
import com.github.hiwepy.qqwry.spring.boot.ext.QQWry;
import com.qihetimes.earthangel.common.ApiException;
import com.qihetimes.earthangel.mapper.UserMapper;
import com.qihetimes.earthangel.model.*;
import com.qihetimes.earthangel.model.bo.UserBO;
import com.qihetimes.earthangel.model.bo.UserSearchBO;
import com.qihetimes.earthangel.model.bo.UserWxBO;
import com.qihetimes.earthangel.model.vo.PageVO;
import com.qihetimes.earthangel.model.vo.UserCenterVO;
import com.qihetimes.earthangel.model.vo.UserVO;
import com.qihetimes.earthangel.service.*;
import com.qihetimes.earthangel.util.IpUtil;
import lombok.SneakyThrows;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author f4cklangzi@gmail.com
 * @since 2021-12-04
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    
    @Resource
    private WxMaService wxMaService;
    
    @Resource
    private RedisService redisService;
    
    @Resource
    private SmsService smsService;
    
    @Resource
    private HttpServletRequest request;
    
    @Resource
    private IGoodsService goodsService;
    
    @Resource
    private IGoodsFavoriteService goodsFavoriteService;
    
    @Resource
    private IGoodsCommentService goodsCommentService;
    
    @Resource
    private IGoodsLikeService goodsLikeService;
    
    @Resource
    private IFeedbackService feedbackService;
    
    @Resource
    private IFriendRelationService friendRelationService;
    
    @Resource
    private IConversationService conversationService;
    
    @Override
    @SneakyThrows
    public String wxLogin(UserWxBO param) {
        WxMaPhoneNumberInfo phoneNoInfo = wxMaService.getUserService().getNewPhoneNoInfo(param.getCode());
        if (phoneNoInfo == null) {
            throw new ApiException("code错误!");
        }
        String phone = phoneNoInfo.getPurePhoneNumber();
        return loginByPhone(phone);
    }
    
    @Override
    @SneakyThrows
    public String jsCodeLogin(UserWxBO param) {
        WxMaJscode2SessionResult res = wxMaService.jsCode2SessionInfo(param.getCode());
        String openid = res.getOpenid();
        User user = lambdaQuery().eq(User::getOpenid, openid).one();
        if (user == null) {
            throw new ApiException("用户未绑定openid,请绑定后再使用jsCode登录!");
        }
        String token = IdUtil.fastUUID();
        redisService.set(token, user.getId(), 3600 * 24 * 365L);
        return token;
    }
    
    @Override
    @SneakyThrows
    public void bindOpenId(UserWxBO param) {
        User loginUser = getLoginUser();
        WxMaJscode2SessionResult res = wxMaService.jsCode2SessionInfo(param.getCode());
        String openid = res.getOpenid();
        loginUser.setOpenid(openid);
        updateById(loginUser);
    }
    
    @Override
    @SneakyThrows
    public String codeLogin(String phone, Integer code) {
        boolean res = smsService.checkCode(phone, code);
        if (!res) {
            throw new ApiException("验证码错误!");
        }
        return loginByPhone(phone);
    }
    
    private String loginByPhone(String phone) {
        User user = lambdaQuery().eq(User::getPhone, phone).one();
        if (user == null) {
            user = new User();
            user.setId(IdUtil.objectId());
            user.setPhone(phone);
            String nickName = DesensitizedUtil.mobilePhone(phone);
            user.setNickName(nickName);
            user.setAvatarUrl("https://api.prodless.com/avatar.png");
            user.setIntegral(0);
            user.setLevel(0);
            user.setCreatedAt(LocalDateTime.now());
            save(user);
        }
        String token = IdUtil.fastUUID();
        redisService.set(token, user.getId(), 3600 * 24 * 365L);
        return token;
    }
    
    @Override
    public User getLoginUser() {
        return (User) request.getAttribute("user");
    }
    
    @Override
    public UserCenterVO userCenter() {
        UserCenterVO userCenter = new UserCenterVO();
        User user = getLoginUser();
        userCenter.setUser(user);
        Integer publishCount = goodsService.lambdaQuery().eq(Goods::getUserId, user.getId()).count();
        userCenter.setPublishCount(publishCount);
        Integer getCount = goodsService.lambdaQuery().eq(Goods::getToUserId, user.getId()).count();
        userCenter.setGetCount(getCount);
        Integer favoriteCount = goodsFavoriteService.lambdaQuery().eq(GoodsFavorite::getUserId, user.getId()).count();
        userCenter.setFavoriteCount(favoriteCount);
        int unreadCount = conversationService.lambdaQuery()
            .eq(Conversation::getToUserId, user.getId()).list()
            .stream().mapToInt(Conversation::getUnreadCount).sum();
        userCenter.setUnreadMessageCount(unreadCount);
        return userCenter;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public User update(UserBO param) {
        User loginUser = getLoginUser();
        CopyOptions options = CopyOptions.create();
        options.ignoreNullValue();
        BeanUtil.copyProperties(param, loginUser, options);
        loginUser.setUpdatedAt(LocalDateTime.now());
        updateById(loginUser);
        String userId = loginUser.getId();
        goodsCommentService.lambdaUpdate().eq(GoodsComment::getUserId, userId)
            .set(GoodsComment::getNickName, loginUser.getNickName())
            .set(GoodsComment::getAvatarUrl, loginUser.getAvatarUrl())
            .update();
        goodsFavoriteService.lambdaUpdate().eq(GoodsFavorite::getUserId, userId)
            .set(GoodsFavorite::getNickName, loginUser.getNickName())
            .set(GoodsFavorite::getAvatarUrl, loginUser.getAvatarUrl())
            .update();
        goodsLikeService.lambdaUpdate().eq(GoodsLike::getUserId, userId)
            .set(GoodsLike::getNickName, loginUser.getNickName())
            .set(GoodsLike::getAvatarUrl, loginUser.getAvatarUrl())
            .update();
        feedbackService.lambdaUpdate().eq(Feedback::getUserId, userId)
            .set(Feedback::getNickName, loginUser.getNickName())
            .set(Feedback::getAvatarUrl, loginUser.getAvatarUrl())
            .update();
        conversationService.lambdaUpdate().eq(Conversation::getFromUserId, userId)
            .set(Conversation::getFromUserNickName, loginUser.getNickName())
            .set(Conversation::getFromUserAvatarUrl, loginUser.getAvatarUrl())
            .update();
        conversationService.lambdaUpdate().eq(Conversation::getToUserId, userId)
            .set(Conversation::getToUserNickName, loginUser.getNickName())
            .set(Conversation::getToUserAvatarUrl, loginUser.getAvatarUrl())
            .update();
        return loginUser;
    }
    
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete() {
        User loginUser = getLoginUser();
        String userId = loginUser.getId();
        removeById(userId);
        goodsService.lambdaQuery().eq(Goods::getUserId, userId).list().forEach(goods -> {
            goodsService.delete(goods.getId());
        });
    }
    
    @Override
    public PageVO<UserVO> search(UserSearchBO param) {
        Page<User> queryPage = new Page<>(param.getPageNum(), param.getPageSize());
        String keyword = param.getKeyword();
        lambdaQuery().and(query -> query.like(User::getPhone, keyword)
            .or()
            .like(User::getNickName, keyword)).page(queryPage);
        PageVO<UserVO> res = new PageVO<>(queryPage);
        if (queryPage.getRecords().isEmpty()) {
            return res;
        }
        User loginUser = getLoginUser();
        PageVO<UserVO> friendList = friendRelationService.getFriendList(loginUser.getId(), 1L, Long.MAX_VALUE);
        Set<String> friendSet = friendList.getRecords().stream().map(UserVO::getId).collect(Collectors.toSet());
        List<UserVO> userVOList = queryPage.getRecords().stream()
            .map(user -> {
                UserVO userVO = BeanUtil.toBean(user, UserVO.class);
                userVO.setFriendFlag(userVO.getId().equals(loginUser.getId()) || friendSet.contains(userVO.getId()));
                return userVO;
            }).collect(Collectors.toList());
        res.setRecords(userVOList);
        return res;
    }
    
    @Override
    public void logout() {
        String token = request.getHeader("Authorization");
        redisService.remove(token);
    }
    
    @Override
    @SneakyThrows
    public String ipToCity(String ip) {
        if (ip == null) {
            ip = IpUtil.getClientIpAddr();
        }
        IPZone ipZone = new QQWry().findIP(ip);
        return ipZone.getMainInfo();
    }
}
